package org.maxgroundstation.simulation.service;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

/**
 * Utility class for service communication.
 */
public final class ServiceUtil {

	public static final int MAX_FRAGMENT_SIZE = 10000;
	
	/** 
	 * Private constructor for utility class.
	 */
	private ServiceUtil() {};
	
	/**
	 * Parse the data fragment from an array of data.
	 * @param data the array of data.
	 * @return data fragment.
	 */
	static final ServiceDataFragment parseDataFragment(byte[] data) {
		ServiceDataFragment dataFrag = new ServiceDataFragment();
		
		byte[] headerArray = Arrays.copyOfRange(data, 0, 40);
		dataFrag.setHeader(parseDataFragmentHeader(headerArray));
		
		byte[] dataArray = Arrays.copyOfRange(data, 40, data.length);
		dataFrag.setData(dataArray);
		
		return dataFrag;
	}
	
	/**
	 * Parses a data fragment header from an array of bytes.
	 * @param data the array of data.
	 * @return data fragment header.
	 */
	static final ServiceDataFragmentHeader parseDataFragmentHeader(byte[] data) {
		ServiceDataFragmentHeader header = new ServiceDataFragmentHeader();
		
		ByteBuffer buff = ByteBuffer.wrap(data);
		buff.order(ByteOrder.LITTLE_ENDIAN);
		
		long syncHeader = buff.getLong();
		header.setSyncHeader(syncHeader);
		
		StringBuffer serviceIDBuff = new StringBuffer();
		for (int x = 0; x < 8; x++) serviceIDBuff.append((char) buff.get());
		String serviceID = serviceIDBuff.toString();
		header.setServiceID(serviceID);
		
		StringBuffer clientIDBuff = new StringBuffer();
		for (int x = 0; x < 8; x++) clientIDBuff.append((char) buff.get());
		String clientID = clientIDBuff.toString();
		header.setClientID(clientID);
		
		long frameLength = buff.getInt() | 0L;
		header.setFrameLength(frameLength);
		
		int fragSize = buff.getShort() | 0;
		header.setFragSize(fragSize);
		
		int fragNumber = buff.getShort() | 0;
		header.setFragNumber(fragNumber);
		
		long timestamp = buff.getLong();
		header.setTimestamp(timestamp);
		
		return header;
	}
	
	/**
	 * Creates a data fragment.
	 * @param serviceID the service ID.
	 * @param clientID the client ID.
	 * @param frameLength the frame length (bytes).
	 * @param fragNum the fragment number.
	 * @param data the data array.
	 * @return a service data fragment.
	 */
	static final ServiceDataFragment createDataFragment(String serviceID, 
			String clientID, long frameLength, int fragNum, int fragLength, byte[] data) {
		
		ServiceDataFragment dataFrag = new ServiceDataFragment();
		
		ByteBuffer syncBuff = ByteBuffer.allocate(8);
		syncBuff.order(ByteOrder.LITTLE_ENDIAN);
		for (int x = 0; x < 8; x++) syncBuff.put((byte) 0xFF);
		dataFrag.getHeader().setSyncHeader(syncBuff.getLong(0));
		
		dataFrag.getHeader().setServiceID(serviceID);
		
		dataFrag.getHeader().setClientID(clientID);
		
		dataFrag.getHeader().setFrameLength(frameLength);
		
		dataFrag.getHeader().setFragSize(fragLength);
		
		dataFrag.getHeader().setFragNumber(fragNum);
		
		dataFrag.getHeader().setTimestamp(System.currentTimeMillis());
		
		dataFrag.setData(data);
		
		return dataFrag;
	}
	
	/**
	 * Creates a data frame.
	 * @param serviceID the service ID.
	 * @param clientID the client ID.
	 * @param data the data array.
	 * @return data frame.
	 */
	static final ServiceDataFrame createDataFrame(String serviceID, 
			String clientID, byte[] data) {
		ServiceDataFrame dataFrame = new ServiceDataFrame();
		
		int numFrags = (int) Math.ceil((double) data.length / (double) MAX_FRAGMENT_SIZE);
		for (int x = 0; x < numFrags; x++) {
			int fragLength = 0;
			byte[] fragData = null;
			
			if (numFrags == 1) {
				fragLength = data.length;
				fragData = data;
			}
			else {
				int offset = x * MAX_FRAGMENT_SIZE;
				fragLength = MAX_FRAGMENT_SIZE;
				
				if (x < (numFrags - 1)) {
					fragData = Arrays.copyOfRange(data, offset, fragLength + offset);
				}
				else {
					int trueFragLength = data.length - offset;
					fragData = new byte[trueFragLength];
					//fragData = new byte[MAX_FRAGMENT_SIZE];
					for (int y = 0; y < trueFragLength; y++) {
						fragData[y] = data[y + offset];
					}
				}
			}
			
			ServiceDataFragment fragment = createDataFragment(serviceID, clientID, 
					(long) data.length, x, fragLength, fragData);
			dataFrame.addDataFragment(fragment);
		}
		
		return dataFrame;
	}
	
	/**
	 * Gets the service ID from a base service ID with proceeding bytes.
	 * @param baseServiceID the base service ID.
	 * @return the service ID.
	 */
	public static final String getServiceID(String baseServiceID) {
		StringBuffer buff = new StringBuffer(baseServiceID);
		if (baseServiceID.length() < 8) {
			buff.append(new String(new byte[8 - baseServiceID.length()]));
		}
		return buff.toString();
	}
}